package com.usvisatrack.services;

import java.io.IOException;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.io.SyndFeedInput;
import com.rometools.rome.io.XmlReader;
import com.usvisatrack.core.dao.model.CheckeeVisa;
import com.usvisatrack.core.dao.model.CmsArticle;
import com.usvisatrack.core.dao.model.CmsArticleOssezContent;
import com.usvisatrack.core.dao.model.ConfApp;
import com.usvisatrack.core.dao.model.SocialMedia;
import com.usvisatrack.core.dao.model.SocialMediaLog;
import com.usvisatrack.core.dao.model.User;
import com.usvisatrack.core.dao.model.Visa;
import com.usvisatrack.core.dao.model.VisaClass;
import com.usvisatrack.core.dao.model.VisaStatusLog;
import com.usvisatrack.core.dao.model.data.VisaEntry;
import com.usvisatrack.core.dao.model.data.VisaStatus;
import com.usvisatrack.core.factories.ConfFactory;
import com.usvisatrack.core.factories.DataFactory;
import com.usvisatrack.core.factories.LogFactory;
import com.usvisatrack.core.factories.SocialMediaFactory;
import com.usvisatrack.core.factories.USFactory;
import com.usvisatrack.core.factories.UserFactory;
import com.usvisatrack.core.factories.VisaFactory;
import com.usvisatrack.services.common.DataCrawl;

import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;

/**
 * MerchandiseDataImporter to read folder in disk and import to DB
 * 
 * @author YuCheng Hu
 *
 */
public class VisaImporter extends DataCrawl {
	private static final Logger logger = LoggerFactory.getLogger(VisaImporter.class);
	private static final String CONF_LAST_RUNTIME = VisaImporter.class.getName() + "-lastRuntime";
	private static final String CONF_TWITTER_COUNT = VisaImporter.class.getName() + "-twitterCount";

	private static final String RSS_URL = "http://rss.ossez.com/public.php?op=rss&id=-2&view-mode=all_articles&key=ofclrs5a01d7c3bdab2";

	private static final String TWITTER = "Twitter";
	private static final String URL_CHECKEE = "https://www.checkee.info/main.php?dispdate=";

	private static HashMap<String, VisaClass> visaClassMap = new HashMap<String, VisaClass>();
	private static DateTime lastRuntime = new DateTime();
	private static Integer twitterCount = 0;

	private static boolean webCrawl = false;

	/**
	 * Command process function
	 */
	@Override
	public void parseCommandLine(String[] args) {
		options.addOption("l", true, "Clean all agents input information");
		options.addOption("h", true, "Clean one agent by input email address");

		CommandLineParser parser = new DefaultParser();

		// parse command line
		try {
			CommandLine cmd = parser.parse(options, args);

			if (cmd.hasOption("s")) {
				// this.minId = Integer.parseInt(cmd.getOptionValue("l"));
				// this.maxId = Integer.parseInt(cmd.getOptionValue("h"));
			}

		} catch (Exception e) {
			logger.warn("CLI Parse failed, using default timestamp", e);
		}
	}

	/**
	 * Run Function
	 */
	@Override
	public void run() {
		processInit(); // Init data from database

		// RUN DATA Crawl by last run time
		if (lastRuntime.plusHours(23).isBeforeNow()) {
			crawlWebVisa(URL_CHECKEE + DateTimeFormat.forPattern("yyyy-MM").print(new DateTime()));
			crawlWebVisa(URL_CHECKEE + DateTimeFormat.forPattern("yyyy-MM").print(new DateTime().minusMonths(1)));

			webCrawl = true;
		} else {
			logger.debug("NOT RUN");
		}

		socialMedia();// SOCIAL MEDIA
		rssArticle(); // RSS

		processCleanup();
	}

	/**
	 * INIT PROCESS
	 */
	private void processInit() {
		logger.error("Init Data from Database");

		// LAST RUNTIME
		ConfApp confAppLastRuntime = ConfFactory.get(CONF_LAST_RUNTIME);
		if (confAppLastRuntime != null) {
			lastRuntime = new DateTime(confAppLastRuntime.getValue());
		} else {
			confAppLastRuntime = new ConfApp();
			confAppLastRuntime.setName(CONF_LAST_RUNTIME);
			confAppLastRuntime.setValue(new DateTime().toString());
			ConfFactory.save(confAppLastRuntime);
		}
		logger.debug("Last Runtime - [{}]", lastRuntime);

		// TWITTER COUNT
		ConfApp confAppTwitterCount = ConfFactory.get(CONF_TWITTER_COUNT);
		if (confAppTwitterCount != null) {
			twitterCount = NumberUtils.createInteger(confAppTwitterCount.getValue());
		} else {
			confAppTwitterCount = new ConfApp();
			confAppTwitterCount.setName(CONF_TWITTER_COUNT);
			confAppTwitterCount.setValue("999");
			ConfFactory.save(confAppTwitterCount);
		}
		logger.debug("Last Runtime - [{}]", lastRuntime);

		// VISA CLASS MAP
		List<VisaClass> visaClassList = VisaFactory.getAllVisaClass();
		for (VisaClass visaClass : visaClassList) {
			visaClassMap.put(visaClass.getVisaClassName(), visaClass);
		}

		logger.debug("XXX [{}]", visaClassMap.get("H1-B").getVisaClassEligibility());
	}

	/**
	 * 
	 */
	private void processCleanup() {
		logger.error("Init Data from Database");

		ConfApp confAppLastRuntime = ConfFactory.get(CONF_LAST_RUNTIME);

		if (webCrawl && confAppLastRuntime != null) {
			confAppLastRuntime.setValue(new DateTime().toString());
			ConfFactory.save(confAppLastRuntime);
		}

		confAppLastRuntime = ConfFactory.get(CONF_TWITTER_COUNT);
		if (confAppLastRuntime != null) {
			confAppLastRuntime.setValue(String.valueOf(twitterCount + 1));
			ConfFactory.save(confAppLastRuntime);
		}

	}

	/**
	 * crawlWebItem by parse web page
	 */
	private void crawlWebVisa(String cURL) {
		logger.error("Crawl Web Data to load item info.");

		boolean isNewVisa = false;

		Document doc = null;

		try {
			doc = Jsoup.connect(cURL).get();
			Elements newsHeadlines = doc.select("table");
			Element table = newsHeadlines.get(6);

			// int i = 0;

			for (Element row : table.select("tr")) {
				// i++;
				Elements tds = row.select("td");

				if (!StringUtils.equalsIgnoreCase("ID", tds.get(1).text())) {
					String checkeeCaseNumber = getCheckeeCaseNumber(tds.get(0));

					Visa visa = VisaFactory.getVisaFromCheckee(NumberUtils.toLong(checkeeCaseNumber));

					if (visa == null) {
						visa = new Visa();
						isNewVisa = true;
					}

					// SET VISA CLASS
					String visaClassName = StringUtils.trimToEmpty(tds.get(2).text());
					if (StringUtils.isNotBlank(visaClassName)) {
						switch (visaClassName) {
						case "B1":
							visaClassName = "B-1";
							break;
						case "B2":
							visaClassName = "B-2";
							break;
						case "H1":
							visaClassName = "H1-B";
							break;
						case "H4":
							visaClassName = "H-4";
							break;
						case "F1":
							visaClassName = "F-1";
							break;
						case "F2":
							visaClassName = "F-2";
							break;
						case "J1":
							visaClassName = "J-1";
							break;
						case "L1":
							visaClassName = "L-1";
							break;
						}

						visa.setVisaClass(visaClassMap.get(visaClassName));
					}

					// SET VISA ENTRY
					String visaEntryName = StringUtils.upperCase(StringUtils.trimToEmpty(tds.get(3).text()));
					if (StringUtils.isNotBlank(visaEntryName)) {
						switch (visaEntryName) {
						case "NEW":
							visa.setVisaEntry(VisaEntry.NEW);
							break;
						case "RENEWAL":
							visa.setVisaEntry(VisaEntry.RENEWAL);
							break;
						}

					}

					// SET US Embassy
					String usEmbassyName = StringUtils.upperCase(StringUtils.trimToEmpty(tds.get(4).text()));
					if (StringUtils.isNotBlank(usEmbassyName)) {
						switch (usEmbassyName) {
						case "BEIJING":
							visa.setUsEmbassy(USFactory.searchUSEmbassy("China", "BeiJing"));
							break;
						case "CHENGDU":
							visa.setUsEmbassy(USFactory.searchUSEmbassy("China", "Chengdu"));
							break;
						case "GUANGZHOU":
							visa.setUsEmbassy(USFactory.searchUSEmbassy("China", "Guangzhou"));
							break;
						case "SHANGHAI":
							visa.setUsEmbassy(USFactory.searchUSEmbassy("China", "Shanghai"));
							break;
						case "SHENYANG":
							visa.setUsEmbassy(USFactory.searchUSEmbassy("China", "Shenyang"));
							break;
						case "WUHAN":
							visa.setUsEmbassy(USFactory.searchUSEmbassy("China", "Wuhan"));
							break;
						case "HONGKONG":
							visa.setUsEmbassy(USFactory.searchUSEmbassy("China", "HongKong"));
							break;
						}

					}

					// SET VISA MAJOR
					visa.setMajor(StringUtils.trimToEmpty(tds.get(5).text()));
					logger.debug("[{}]", visa.getMajor());

					// SET VISA STATUS
					String visaStatusName = StringUtils.upperCase(StringUtils.trimToEmpty(tds.get(6).text()));
					if (StringUtils.isNotBlank(visaStatusName)) {
						switch (visaStatusName) {
						case "CLEAR":
							visa.setVisaStatus(VisaStatus.ISSUED);
							break;
						case "PENDING":
							visa.setVisaStatus(VisaStatus.ADMINISTRATIVE_PROCESSING);
							break;
						case "REJECT":
							visa.setVisaStatus(VisaStatus.REFUSED);
							break;
						}

					}

					// SET VISA Date
					String dateVisaInterview = StringUtils.upperCase(StringUtils.trimToEmpty(tds.get(7).text()));
					String dateVisaIssued = StringUtils.upperCase(StringUtils.trimToEmpty(tds.get(8).text()));
					if (StringUtils.isNotBlank(dateVisaInterview)) {
						visa.setDateVisaInterview(DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime(dateVisaInterview).toDate());
					}
					if (StringUtils.isNotBlank(dateVisaIssued) && !StringUtils.equals(dateVisaIssued, "0000-00-00")) {
						visa.setDateVisaIssued(DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime(dateVisaIssued).toDate());
					}

					updateVisaNote(visa, checkeeCaseNumber);
					visa.setModifyDate(new Date());
					visa.setUser(getUser(checkeeCaseNumber));
					VisaFactory.save(visa);
					updateVisaLog(visa.getId());

					if (isNewVisa) {
						CheckeeVisa checkeeVisa = new CheckeeVisa();
						checkeeVisa.setId(NumberUtils.toLong(checkeeCaseNumber));
						checkeeVisa.setVisaID(visa.getId());
						VisaFactory.save(checkeeVisa);
					}

				}

				// if (i == 2)
				// break;

			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void socialMedia() {
		List<VisaStatusLog> visaStatusLogList = LogFactory.getVisaStatusLogAfter(new DateTime().minusHours(2));

		// Twitter
		logger.debug("TOTAL NEED TWITTER - [{}]", visaStatusLogList.size());
		for (VisaStatusLog visaStatusLog : visaStatusLogList) {
			publishTwitter(visaStatusLog);
		}

		for (int i = twitterCount; i < twitterCount + 3; i++) {
			VisaStatusLog visaStatusLog = LogFactory.getVisaStatusLog(NumberUtils.toLong(String.valueOf(i)));
			if (visaStatusLog != null) {
				publishTwitter(visaStatusLog);
			}

		}

	}

	private void rssArticle() {

		try {
			URL urls = new URL(RSS_URL);

			SyndFeedInput input = new SyndFeedInput();
			SyndFeed feed = input.build(new XmlReader(urls));

			for (SyndEntry entry : feed.getEntries()) {

				CmsArticle cmsArticle = null;

				String aid = StringUtils.substringBetween(entry.getLink(), "article-", "-");

				logger.debug("article Id/Title - [{}]/[{}]", aid, entry.getTitle());
				DateTime dt = new DateTime(entry.getUpdatedDate());

				CmsArticleOssezContent cmsArticleOssezContent = DataFactory.getCmsArticleOssezContent(NumberUtils.toLong(aid));
				cmsArticle = DataFactory.getCmsArticle(aid);

				if (cmsArticle == null) {
					cmsArticle = new CmsArticle();
					cmsArticle.setRssArticleId(aid);
				} else if (dt.compareTo(new DateTime(cmsArticle.getRssDateUpdate())) == 0) {
					continue;

				}

				cmsArticle.setTitle(entry.getTitle());
				cmsArticle.setSummary(entry.getContents().get(0).getValue());
				cmsArticle.setContent(cmsArticleOssezContent.getContent());
				cmsArticle.setRssDateUpdate(dt.toDate());

				DataFactory.save(cmsArticle);

				// logger.debug("SAVE");
				// break;

			}

		} catch (Exception ex) {
			logger.error("RSS data update error.", ex);
			//
		}

		// System.out.println(feed);

	}

	private void publishTwitter(VisaStatusLog visaStatusLog) {
		SocialMedia socialMedia = SocialMediaFactory.get(TWITTER);

		SocialMediaLog socialMediaLog = LogFactory.getSocialMediaLog(socialMedia, visaStatusLog);

		if (socialMediaLog == null) {
			ConfigurationBuilder cb = new ConfigurationBuilder();
			cb.setDebugEnabled(true).setOAuthConsumerKey(socialMedia.getKey()).setOAuthConsumerSecret(socialMedia.getKeySecret())
					.setOAuthAccessToken(socialMedia.getToken()).setOAuthAccessTokenSecret(socialMedia.getTokenSecret());

			Twitter twitter = new TwitterFactory(cb.build()).getInstance();

			Visa visa = VisaFactory.get(visaStatusLog.getVisa().getId());
			visaStatusLog.setVisa(visa);

			DateTime dt = new DateTime();
			String visaStatus = StringUtils.upperCase(StringUtils.replace(visaStatusLog.getVisaStatus().name(), "_", " "));

			StringBuffer twitterContent = new StringBuffer("Case Updated ");
			// VisaClass
			if (visaStatusLog.getVisa().getVisaClass() != null) {
				twitterContent.append(" [" + visaStatusLog.getVisa().getVisaClass().getVisaClassName() + "]");
			} else {
				twitterContent.append(" [ UNKNOW ]");
			}

			// Embassy
			if (visaStatusLog.getVisa().getUsEmbassy() != null) {
				twitterContent.append(" - [ #" + StringUtils.trim(visaStatusLog.getVisa().getUsEmbassy().getCity()) + " ] Status To");
			} else {
				twitterContent.append(" -[ UNKNOW ] Status To");
			}
			// Status
			twitterContent.append(" [ " + visaStatus + " ] in");

			dt = new DateTime(visaStatusLog.getDateVisaLastUpdatedDate());
			twitterContent.append(" [" + dt.toString("yyyy-MM-dd") + "].");
			twitterContent.append(" https://usvisatrack.com/caseinfo/" + visaStatusLog.getVisa().getId());

			// String test = "https://usvisatrack.com Updated [H1-B]case for [BEIJING] embassy to status [ISSUED] in 2017-09-28. Click
			// https://usvisatrack.com/caseinfo/31339";

			try {
				logger.debug(twitterContent.toString());
				Status status = twitter.updateStatus(twitterContent.toString());
				logger.debug("TEXT - {}", status.getId());

				socialMediaLog = new SocialMediaLog();
				socialMediaLog.setSocialMedia(socialMedia);
				socialMediaLog.setVisaStatusLog(visaStatusLog);
				socialMediaLog.setContentPublished(twitterContent.toString());
				socialMediaLog.setDatePublished(new Date());
				socialMediaLog.setResponseUuid("" + status.getId());

				SocialMediaFactory.save(socialMediaLog);
			} catch (Exception e) {
				// TODO: handle exception
			}

		} else {
			logger.debug("Content for [{}] already sent in [{}] for ID - [{}].", socialMedia.getName(), socialMediaLog.getCreateDate(),
					socialMediaLog.getId());
		}

		// List<Status> statuses = twitter.getUserTimeline();
		// for (Status status : statuses) {
		// System.out.println("@" + status.getUser().getScreenName() + " - " + status.getText());
		// }
	}

	/**
	 * Get CheckeeCaseNumber from Checkee website
	 * 
	 * @param element
	 * @return
	 */
	private String getCheckeeCaseNumber(Element element) {
		String checkeeCaseNumber = null;
		Element link = element.select("a").first();
		checkeeCaseNumber = StringUtils.substringAfterLast(link.attr("href"), "casenum=");

		return checkeeCaseNumber;
	}

	/**
	 * 
	 * @param userName
	 * @return
	 */
	private User getUser(String checkeeCaseNumber) {
		String userName = null;
		String userEmail = null;

		User user = null;
		Document doc = null;

		try {
			doc = Jsoup.connect("https://www.checkee.info/update.php?casenum=" + checkeeCaseNumber).get();
			Elements elements = doc.select("input[name=email_dis]");
			userEmail = elements.first().val();

			elements = doc.select("b");
			userName = StringUtils.trim(StringUtils.substringAfter(elements.get(7).text(), "ID:"));

			user = UserFactory.get(userName);
			if (user == null) {
				user = new User();
				user.setUserName(userName);
				user.setEmail(userEmail);
				UserFactory.save(user);
			}

		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

		return user;
	}

	private void updateVisaNote(Visa visa, String checkeeCaseNumber) {
		Document doc = null;

		try {
			doc = Jsoup.connect("https://www.checkee.info/personal_detail.php?casenum=" + checkeeCaseNumber).get();
			Elements newsHeadlines = doc.select("table");
			Element table = newsHeadlines.get(2);

			for (Element row : table.select("tr")) {
				Elements tds = row.select("td");
				String dataString = tds.get(0).text();
				if (StringUtils.startsWithIgnoreCase(dataString, "Note:")) {
					visa.setDescription(StringUtils.trimToEmpty(StringUtils.substringAfter(dataString, "Note:")));
				}
			}

		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}

		logger.debug("Case Description - [{}]", visa.getDescription());
	}

	private void updateVisaLog(Long visaId) {
		logger.debug("Update Visa ID - [{}]", visaId);
		try {
			Visa visa = VisaFactory.get(visaId);

			// IF VISA LOG NOT IN SYSTEM
			if (visa != null && visa.getVisaStatusLogSet().isEmpty()) {

				VisaStatusLog visaStatusLog = new VisaStatusLog();
				visaStatusLog.setVisa(visa);
				visaStatusLog.setVisaStatus(VisaStatus.ADMINISTRATIVE_PROCESSING);
				visaStatusLog.setDateVisaLastUpdatedDate(visa.getDateVisaInterview());

				VisaFactory.save(visaStatusLog);

				if (visa.getDateVisaIssued() != null
						&& visa.getVisaStatus() != visa.getVisaStatusLogSet().iterator().next().getVisaStatus()) {
					visaStatusLog = new VisaStatusLog();
					visaStatusLog.setVisa(visa);
					visaStatusLog.setVisaStatus(visa.getVisaStatus());
					visaStatusLog.setDateVisaLastUpdatedDate(visa.getDateVisaIssued());

					VisaFactory.save(visaStatusLog);
				}

			} else {
				if (visa.getVisaStatus() != visa.getVisaStatusLogSet().iterator().next().getVisaStatus()) {
					VisaStatusLog visaStatusLog = new VisaStatusLog();
					visaStatusLog.setVisa(visa);
					visaStatusLog.setVisaStatus(visa.getVisaStatus());
					visaStatusLog.setDateVisaLastUpdatedDate(visa.getDateVisaIssued());

					VisaFactory.save(visaStatusLog);
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
